#ifndef _Load_Store_Buffer
#define _Load_Store_Buffer
#include <iostream>
#include <cstring>
#include <cstdio>
#include "store.hpp"
#include "arithmetic.hpp"
#include "tomasulo_cpu.hpp"

void CPU::LSB_run()
{
    #ifdef func
        puts("");
        puts("---LSB_run---");
    #endif 
    CPU::LSB_new = CPU::LSB_prev;

    CPU::Load_Store_Buffer::node t = CPU::issue_to_LSB;

    CPU::CDB c = CPU::ex_to_CDB;

    if (~c.dest)
    {
        for (int i = 0; i <= CPU::queue_len; i++)
            if (CPU::LSB_prev.h.busy[i])
            {
                if (CPU::LSB_prev.h.node[i].Qj == c.dest)
                {
                    CPU::LSB_new.h.node[i].Vj = c.val;
                    CPU::LSB_new.h.node[i].Qj = -1;
                }

                if (CPU::LSB_prev.h.node[i].Qk == c.dest)
                {
                    CPU::LSB_new.h.node[i].Vk = c.val;
                    CPU::LSB_new.h.node[i].Qk = -1;
                }
            }
        
        if (t.Qj == c.dest)
        {
            t.Vj = c.val;
            t.Qj = -1;
        }
        if (t.Qk == c.dest)
        {
            t.Vk = c.val;
            t.Qk = -1;
        }
    }

    c = CPU::LSB_to_CDB_prev;
    if (~c.dest)
    {
        for (int i = 0; i <= CPU::queue_len; i++)
            if (CPU::LSB_prev.h.busy[i])
            {
                if (CPU::LSB_prev.h.node[i].Qj == c.dest)
                {
                    CPU::LSB_new.h.node[i].Vj = c.val;
                    CPU::LSB_new.h.node[i].Qj = -1;
                }

                if (CPU::LSB_prev.h.node[i].Qk == c.dest)
                {
                    CPU::LSB_new.h.node[i].Vk = c.val;
                    CPU::LSB_new.h.node[i].Qk = -1;
                }
            }

        if (t.Qj == c.dest)
        {
            t.Vj = c.val;
            t.Qj = -1;
        }
        if (t.Qk == c.dest)
        {
            t.Vk = c.val;
            t.Qk = -1;
        }
    }

    if (~t.opcode)
        CPU::LSB_new.h.push(t);

    if (CPU::commit_to_LSB)
    {
        Cycle_Queue<CPU::Load_Store_Buffer::node, CPU::queue_len> h = CPU::LSB_prev.h;
        for (int i = h.get_next(h.front); i != h.get_next(h.rear); i = h.get_next(i))
        if (h.node[i].opcode == 35 && !h.node[i].iscommit)
        {
            CPU::LSB_new.h.node[i].iscommit = 1;
            break;
        }
    }

    if (CPU::commit_signal < 0)
    {
        while (!LSB_new.h.empty() && !LSB_new.h.get_rear().iscommit)
            LSB_new.h.pop_rear();
    }

    if (!LSB_prev.h.empty() && !LSB_new.h.empty())
    {
        const CPU::Load_Store_Buffer::node &p_prev = CPU::LSB_prev.h.get_front();
        CPU::Load_Store_Buffer::node &p_new = CPU::LSB_new.h.get_front();

        if (~p_prev.Qj) return;
        if (p_prev.opcode == 35 && ~p_prev.Qk) return;

        if (p_prev.tim == -1)
        {
            if (p_prev.opcode == 3)
                p_new.tim = 3;
            else
            {
                if (p_prev.iscommit)
                    p_new.tim = 3;
                else
                {
                    if (~p_prev.pos)
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, 1, -1);
                    p_new.pos = -1; // 表示已经发送过 CDB
                    p_new.tim = -1;
                }
            }
        }
        else
        {
            p_new.tim = p_prev.tim - 1;
            if (p_new.tim == 0)
            {
                CPU::LSB_new.h.pop();
                uint tmp = p_prev.Vj + p_prev.A;

                if (p_prev.opcode == 3) // LB ~ LHU
                {
                    if (p_prev.func3 == 0) // LB
                    {
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, sext(CPU::M.mem[tmp], 7));    
                    }
                    else if (p_prev.func3 == 1) // LH
                    {
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, sext(CPU::M.mem[tmp] + (CPU::M.mem[tmp + 1] << 8), 15));
                    }
                    else if (p_prev.func3 == 2) // LW
                    {
                        uint val = 0;
                        for (int i = 3; ~i; i--)
                            val = val << 8 | CPU::M.mem[tmp + i];
                        
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, val);
                    }
                    else if (p_prev.func3 == 4) // LBU
                    {
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, CPU::M.mem[tmp]);    
                    }
                    else if (p_prev.func3 == 5) // LHU
                    {
                        CPU::LSB_to_CDB_new = CPU::CDB(p_prev.dest, CPU::M.mem[tmp] + (CPU::M.mem[tmp + 1] << 8));
                    }
                }
                else if (p_prev.opcode == 35) // SB ~ SW
                {
                    if (p_prev.func3 == 0) // SB
                    {
                        CPU::M.mem[tmp] = p_prev.Vk & ((1 << 8) - 1);
                    }
                    else if (p_prev.func3 == 1) // SH
                    {
                        uint val = p_prev.Vk;
                        for (int i = 0; i < 2; i++)
                            CPU::M.mem[tmp + i] = val & ((1 << 8) - 1), val >>= 8;
                    }
                    else if (p_prev.func3 == 2) // SW
                    {
                        uint val = p_prev.Vk;
                        // printf("tmp:%d val:%u\n", tmp, val);
                        for (int i = 0; i < 4; i++)
                            CPU::M.mem[tmp + i] = val & ((1 << 8) - 1), val >>= 8;
                    }
                }
            }
        }
    }
    #ifdef func
        puts("-------------");
        puts("");
    #endif 
}

#endif